// gameview.cpp
//

#include "gameview.hpp"

  

// GameView::GameView
//
GameView::GameView ( wtk::Display &display,
                     Scene &scene )
  : wtk::Dialog(display),
    scene(scene)
{
  setup_renderer();
}



// GameView::setup_renderer
//
void GameView::setup_renderer ()
{
  ProgramID prog_object_raw = renderer.register_program("object_raw",
                                                        datadir + "/shaders/tex_raw.vert",
                                                        datadir + "/shaders/tex_raw.frag");

  PipelineID pipeline_object_raw = renderer.register_pipeline("object_raw",
                                                              prog_object_raw);

  SceneManager &manager = scene.get_manager();
  EntityFamilyID object_family_id = manager.get_entity_family_id("object");
  renderer.register_render_mode("raw",
                                {
                                  {object_family_id, pipeline_object_raw},
                                });
}


// GameView::render_init
//
void GameView::render_init ()
{
  // texture.load(datadir + "/textures/default_grass.png");
  texture.load(datadir + "/textures/default_silver_sandstone.png");
  texture.upload();

  // if ((uloc_col_factor = gl.GetUniformLocation(program.get_id(), "colFactor")) < 0)
  //   WGL_FATAL("uniform location not found");

  axis.axis();
  axis.scale(4.f);
  axis_buffer.upload(axis);

  marker.load(datadir + "/models/sphere.obj");
  marker.scale(2.f);
  marker_buffer.upload(marker);
    
  light_model.cube();
  light_model.scale(0.25f);
  light_buffer.upload(light_model);
    
  // suzanne.load(datadir + "/models/suzanne.obj");
  // suzanne.scale(1.5f);
  // suzanne.uv_scale(10.f);

  suzanne.load(datadir + "/models/suzanne.obj");
  suzanne.scale(1.5f);
  suzanne.uv_scale(4.f);

  suzanne_buffer.upload(suzanne);
    
  // uncomment this call to draw in wireframe polygons.
  // gl.PolygonMode(GL_FRONT_AND_BACK, GL_LINE);

  // camera
  GLint viewport[4];
  gl.GetIntegerv(GL_VIEWPORT, viewport);

  camera.perspective(glm::radians(45.f),
                     float(viewport[2]) / float(viewport[3]),
                     .1f, 100.f);

  camera.set_target_position({0.f, 0.f, .0f});
  camera.set_pitch(glm::radians(30.f));
  camera.set_yaw(glm::radians(60.f));
  camera.set_distance(float(cam_dist));

  // scene_lighting.ambient = {.8f, .9f, 1.f, 1.f};
  // scene_lighting.diffuse_vector = {1.f, 1.f, 1.f, 1.f};
  // scene_lighting.diffuse_vector = glm::normalize(scene_lighting.diffuse_vector);
}



// GameView::render
//
void GameView::render ( const DrawContext &context )
{
  float df = float(frame_count /*% 360*/);
  float rf = glm::radians(df);
  float rfx = std::cos(rf);
  float rfz = std::sin(rf);

  camera.set_yaw(rf * 0.1f);
  // camera.set_target_position({0.f, 0.f, df*0.01f});
  // camera.set_distance(5.f + df*.1f);
    
  gl.Enable(GL_DEPTH_TEST);
  gl.ClearDepth(1.f);
  gl.ClearColor(0.f, 0.f, 0.f, 1.f);
  gl.Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glm::mat4 cam_vp = camera.get_vp();

  // light object
  light_transfo.set_position({10.f * rfx, 3.f, 10.f * rfz});
  light_transfo.set_rotation({0.f, -rf, 0.f});

  // scene lighting
  float ambient_strength = .25f; //(std::sin(rf) + 1.f) * .5f * .66f + .33f;
  scene_lighting.ambient = {.7f, .8f, .9f, ambient_strength};
  scene_lighting.light_pos = light_transfo.position();
  // scene_lighting.diffuse_dir = glm::vec4(light_transfo.position(), 1.f);
  // scene_lighting.diffuse_dir = glm::normalize(scene_lighting.diffuse_dir);
    
  // light source
  prog_light_source.use();
  prog_light_source.m_proj.setm4(cam_vp);
  prog_light_source.m_model.setm4(light_transfo.matrix());
  light_buffer.render(GL_TRIANGLES);
    
  // models
  prog_model.use();
  prog_model.m_proj.setm4(cam_vp);
  prog_model.ambient.setv4(scene_lighting.ambient);
  prog_model.light_pos.setv3(scene_lighting.light_pos);
  // prog_model.diffuse_dir.setv4(scene_lighting.diffuse_dir);

  gl.Enable(GL_TEXTURE_2D);

  // suzanne
  suzanne_transfo.set_position({0.f, 0.f, 0.f});
  suzanne_transfo.set_rotation({0.f, glm::radians(-90.f), 0.f});
  // suzanne_transfo.set_rotation({rf, rf * 1.3f, rf * 1.7f});
    
  gl.BindTexture(GL_TEXTURE_2D, texture.get_id());
  prog_model.m_model.setm4(suzanne_transfo.matrix());
  glm::mat3 m_normal = glm::mat3(glm::transpose(glm::inverse(suzanne_transfo.matrix())));
  prog_model.m_normal.setm3(m_normal);
  suzanne_buffer.render(GL_TRIANGLES);

  renderer.render(scene);

  // markers
  {
    glm::vec3 mpos = camera.position();
    glm::mat4 mt{1.f};
    mt = glm::translate(mt, mpos);
    glm::mat3 mn = glm::mat3(glm::transpose(glm::inverse(mt)));
    prog_model.m_model.setm4(mt);
    prog_model.m_normal.setm3(mn);
    gl.PolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    marker_buffer.render(GL_TRIANGLES);
    gl.PolygonMode(GL_FRONT_AND_BACK, GL_FILL);
  }

  //
  gl.Disable(GL_TEXTURE_2D);

  // axis
  // gl.Disable(GL_DEPTH_TEST);

  prog_wire.use();
  prog_wire.m_proj.setm4(cam_vp);

  axis_buffer.render(GL_LINES);

  // cleanup (useless!)
  gl.BindVertexArray(0);
  gl.UseProgram(0);

  gl.Disable(GL_DEPTH_TEST);
}


  
// GameView::draw
//
void GameView::draw ( const DrawContext &context )
{
  static bool initialized = false;
  if (!initialized)
    {
      render_init();
      initialized = true;
    }
    
  render(context);
  
  frame_count++;
}


  
// GameView::event_keyboard
//
void GameView::event_keyboard ( const Event &event )
{
  WTK_TRACE("key: %u", event.keyboard.keycode);
}



// GameView::event_mouse
//
void GameView::event_mouse ( const Event &event )
{
  switch (event.type)
    {
    case EventType::MouseMotion:
      // WTK_TRACE("mouse motion: %d, %d (%d)",
      //           event.motion.xrel,
      //           event.motion.yrel,
      //           event.motion.state);
      break;

    case EventType::MouseButtonDown:
      WTK_TRACE("mouse button down: %d, %d (%d)",
                event.button.x,
                event.button.y,
                event.button.button);
      break;

    case EventType::MouseButtonUp:
      WTK_TRACE("mouse button up: %d, %d (%d)",
                event.button.x,
                event.button.y,
                event.button.button);
      break;

    case EventType::MouseWheel:
      WTK_TRACE("mouse wheel: %d, %d",
                event.wheel.wx,
                event.wheel.wy);
      cam_dist -= event.wheel.wy * 2;
      camera.set_distance(float(cam_dist));
      break;

    default:
      break;
    }
}
